package gredis

import (
	"context"
	"github.com/go-redis/redis"
	"net"
	"regexp"
	"sync/atomic"
)

// Redis主从组
type CACHEGroup struct {
	mCounter uint64
	Master   []*CACHEConn

	sCounter uint64
	Slave    []*CACHEConn

	Cluster *CACHEConn
}

// 创建主从连接
func newMasterSlaveConn(cacheGroup *CACHEGroup, groupConf *CACHEGroupConf) (err error) {
	masterConf := groupConf.Master
	slaveConf := groupConf.Slaves

	cacheGroup.Master = make([]*CACHEConn, 0)
	cacheGroup.Slave = make([]*CACHEConn, 0)

	var cacheConn *CACHEConn

	// master连接池
	if masterConf != nil && masterConf.Instances != nil {
		hostCountMap := cacheGroup.getHostCountMap(masterConf.Instances)
		for i := 0; i < len(masterConf.Instances); i++ {
			if cacheConn, err = newCACHEConnection(groupConf, masterConf, &masterConf.Instances[i], hostCountMap); err != nil {
				return
			}
			cacheGroup.Master = append(cacheGroup.Master, cacheConn)
		}
	}
	// slave连接池
	if slaveConf != nil && slaveConf.Instances != nil {
		hostCountMap := cacheGroup.getHostCountMap(slaveConf.Instances)
		for i := 0; i < len(slaveConf.Instances); i++ {
			if cacheConn, err = newCACHEConnection(groupConf, slaveConf, &slaveConf.Instances[i], hostCountMap); err != nil {
				return
			}
			cacheGroup.Slave = append(cacheGroup.Slave, cacheConn)
		}
	}
	return
}

// 创建集群连接
func newClusterConn(cacheGroup *CACHEGroup, groupConf *CACHEGroupConf) (err error) {
	clusterConf := groupConf.Cluster

	var cacheConn *CACHEConn

	// redis-cluster连接池
	if clusterConf != nil && clusterConf.Instances != nil {
		if cacheConn, err = newCACHEConnection(groupConf, clusterConf, nil, nil); err != nil {
			return
		}
		cacheGroup.Cluster = cacheConn
	}
	return
}

func newCACHEGroup(groupConf *CACHEGroupConf) (cacheGroup *CACHEGroup, err error) {
	//redis实例名字必须设置
	if len(groupConf.Name) <= 0 {
		err = ERR_CACHE_NAME_NOT_FOUND
		return
	}

	cacheGroup = &CACHEGroup{}
	if err = newMasterSlaveConn(cacheGroup, groupConf); err != nil {
		return
	}
	if err = newClusterConn(cacheGroup, groupConf); err != nil {
		return
	}
	return
}

// 选择连接
func (cacheGroup *CACHEGroup) ChooseConn(ctx context.Context, accessType ACC_TYPE) (currentClient *redis.Client, err error) {
	var currentConn *CACHEConn

	// 从库
	if accessType == SLAVE && len(cacheGroup.Slave) > 0 {
		counter := atomic.AddUint64(&cacheGroup.sCounter, 1)
		currentConn = cacheGroup.Slave[counter%uint64(len(cacheGroup.Slave))]
	} else if len(cacheGroup.Master) != 0 {
		counter := atomic.AddUint64(&cacheGroup.mCounter, 1)
		currentConn = cacheGroup.Master[counter%uint64(len(cacheGroup.Master))]
	}

	if currentConn == nil {
		err = ERR_CACHE_CONN_NOT_FOUND
		return
	}

	// 包装请求上下文
	currentClient, err = currentConn.NewWrapperClient(ctx)
	return
}

func (cacheGroup *CACHEGroup) ChooseClusterConn(ctx context.Context) (currentClient *redis.ClusterClient, err error) {
	var currentConn *CACHEConn

	// cluster
	currentConn = cacheGroup.Cluster

	if currentConn == nil {
		err = ERR_CACHE_CONN_NOT_FOUND
		return
	}

	// 包装请求上下文
	currentClient, err = currentConn.NewWrapperClusterClient(ctx)
	return
}

// 多个从库指向1个redis实例 map管理
func (cacheGroup *CACHEGroup) getHostCountMap(instances []CACHEConnConf) (hostCountMap map[string]int) {
	// host => host对应的ip被使用次数
	hostCountMap = make(map[string]int, 0)
	// host => ip 映射关系
	hostIpMap := make(map[string]string, 0)
	// ip => 出现次数映射关系
	ipCountMap := make(map[string]int, 0)
	for i := 0; i < len(instances); i++ {
		ins := instances[i]
		ipStr := "127.0.0.1"
		// 匹配是否是直接配的IP地址
		ipV4Matched, _ := regexp.MatchString("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", ins.Host)
		if ipV4Matched {
			ipStr = ins.Host
		} else {
			addrList, _ := net.LookupIP(ins.Host)
			for _, addr := range addrList {
				if addr.To4() != nil {
					ipStr = addr.String()
					break
				}
			}
		}
		hostIpMap[ins.Host] = ipStr
		_, exists := ipCountMap[ipStr]
		if !exists {
			ipCountMap[ipStr] = 0
		}
		ipCountMap[ipStr] = ipCountMap[ipStr] + 1
	}
	for host, ip := range hostIpMap {
		count := ipCountMap[ip]
		hostCountMap[host] = count
	}
	return
}
